home *** CD-ROM | disk | FTP | other *** search
- /* util.c
- Utility routines to maintain and process gopher data structures
- and events. */
-
- /*---------------------------------------------------------------*/
- /* Xgopher version 1.3 08 April 1993 */
- /* version 1.2 20 November 1992 */
- /* version 1.1 20 April 1992 */
- /* version 1.0 04 March 1992 */
- /* X window system client for the University of Minnesota */
- /* Internet Gopher System. */
- /* Allan Tuchman, University of Illinois at Urbana-Champaign */
- /* Computing and Communications Services Office */
- /* Copyright 1992, 1993 by */
- /* the Board of Trustees of the University of Illinois */
- /* Permission is granted to freely copy and redistribute this */
- /* software with the copyright notice intact. */
- /*---------------------------------------------------------------*/
-
- /* ***** really, this file needs to divide into 3:
- itemPublic.c itemUtil.c itemClass.c
- there are some global variables to consider, though.
- ***** */
-
- #include <stdio.h>
-
- #include "osdep.h"
- #include "conf.h"
- #include "gopher.h"
- #include "globals.h"
- #include "gui.h"
- #include "item.h"
- #include "itemList.h"
- #include "dir.h"
- #include "dirList.h"
- #include "util.h"
- #include "status.h"
- #include "misc.h"
- #include "net.h"
- #include "appres.h"
- #include "jobs.h"
-
-
- #include <ctype.h>
- #include <errno.h>
-
- #include "sc_text.h"
- #include "sc_dir.h"
- #include "sc_cso.h"
- #include "sc_index.h"
- #include "sc_telnet.h"
- #include "sc_tn3270.h"
- #include "sc_binary.h"
- #include "sc_image.h"
- #include "sc_sound.h"
- #include "sc_extend.h"
-
- char prefixUnknown [ PREFIX_LEN ];
-
- scInfo noSubclass = {
- "unknown gopher item type",
- prefixUnknown,
- &nullHostList,
- GI_access,
- GI_copyToFile,
- copyTypeNone,
- GI_process,
- GI_init,
- GI_done,
- GI_restart
- };
-
- #define UNKNOWN_TYPE(gi) ((gi)->sc == &noSubclass)
-
-
-
- typedef struct giSubclassStruct {
- char typeID;
- scInfo *subclass;
- struct giSubclassStruct *next;
- } giSubclass;
-
- /* the order of the following subclasses is important. All directory
- types must come first, then the extended types (so they can
- override internal ones), then all other built-in types. */
-
- static giSubclass definedSubclass[] = {
- {A_DIRECTORY, &dirSubclass, NULL},
- {A_INDEX, &indexSubclass, NULL},
- {A_EXTENDED, &extendSubclass,NULL},
- {A_FILE, &textSubclass, NULL},
- {A_CSO, &csoSubclass, NULL},
- {A_BINARY, &binarySubclass,NULL},
- {A_IMAGE, &imageSubclass, NULL},
- {A_TELNET, &telnetSubclass,NULL},
- {A_TN3270, &tn3270Subclass,NULL},
- {A_SOUND, &soundSubclass, NULL},
- /* '?' type will be Unknown */
- {'?', &noSubclass, NULL},
- {'\0', &noSubclass, NULL},
- };
-
- /* the following can be implemented as extended types:
-
- A_MAC_BINHEX, (mac binhex),
- A_DOS_BINHEX, (dos binhex),
- A_UNIX_UUENCODE, (uuencoded file)
- */
-
- static giSubclass *activeSubclass = NULL;
-
-
- /* ====================================================================== */
- /* */
- /* publicly callable procedures */
- /* */
- /* ====================================================================== */
-
- /* makeItem
- Given the contents, allocate and build a new gopher item */
-
- gopherItemP
- makeItem(t, titleString, selectString, host, port, plus)
- char t;
- char *titleString;
- char *selectString;
- char *host;
- int port;
- BOOLEAN plus;
- {
- gopherItemP gi = newItem();
- giSubclass *scID;
-
- gi->type = t;
- strncpy(USER_STRING(gi), titleString, USER_STRING_LEN);
- vStringSet(&(gi->selector), selectString);
- strncpy(gi->host, host, HOST_STRING_LEN);
- gi->port = port;
- gi->plus = FALSE;
-
- /* determine subclass and set fields determined by those class
- variables and class methods. */
-
- for (scID = activeSubclass;
- scID->typeID != t && scID->typeID != A_UNKNOWN;
- scID=scID->next) ;
- gi->sc = scID->subclass;
-
- PREFIX(gi, gi->sc->typePrefix);
-
- gi->accessOk = gi->sc->checkAccess(gi);
-
- return gi;
- }
-
-
- /* initItemClasses
- initialize each item class - gather its resources, restrictions,
- and if appropriate, create its user interface resources. */
-
- void
- initItemClasses()
- {
- giSubclass *scID;
-
- for (scID = definedSubclass; scID->typeID != '\0'; scID++) {
- scID->subclass->initProc();
- }
-
- #ifdef DEBUG
- fprintf (stderr, "Known item types are:\n");
- for (scID = activeSubclass; scID != NULL; scID=scID->next) {
- fprintf (stderr, "\t%c, %.5s, %s\n",
- scID->typeID, scID->subclass->typePrefix,
- scID->subclass->typeName);
- }
- #endif /* DEBUG */
-
- return;
- }
-
-
- /* doneItemClasses
- terminate each item class - free its resources and user interface
- resources as necessary. */
-
- void
- doneItemClasses()
- {
- giSubclass *scID;
-
- for (scID = activeSubclass; scID != NULL; scID=scID->next) {
- scID->subclass->doneProc();
- }
-
- killAllItemProcesses();
-
- return;
- }
-
-
- /* restartItemClasses
- restart each item class - re-initialize its resources and user
- interface components as necessar. */
-
- void
- restartItemClasses()
- {
- giSubclass *scID;
-
- for (scID = activeSubclass; scID != NULL; scID=scID->next) {
- scID->subclass->restartProc();
- }
-
- return;
- }
-
-
- /* processItem
- check accessability, then process the selected item */
-
- BOOLEAN
- processItem(gi)
- gopherItemP gi;
- {
- BOOLEAN result=FALSE;
-
- if (! gi->accessOk) {
- char message[MESSAGE_STRING_LEN];
-
- sprintf(message,
- "Sorry, %s access is restricted in this session.",
- gi->sc->typeName);
- showError(message);
- result = FALSE;
- } else {
- result = gi->sc->processItem(gi);;
- }
-
- return result;
- }
-
-
- /* copyItemToFile
- check accessability, then copy the selected item to a file */
-
- BOOLEAN
- copyItemToFile(gi)
- gopherItemP gi;
- {
- BOOLEAN result=FALSE;
-
- if (! gi->accessOk) {
- char message[MESSAGE_STRING_LEN];
-
- sprintf(message,
- "Sorry, %s access is restricted in this session.",
- gi->sc->typeName);
- showError(message);
- result = FALSE;
- } else {
- result = gi->sc->copyProc(gi);;
- }
-
- return result;
- }
-
-
- /* updateDirectory
- reload an existing gopher directory (possibly an index search result) */
-
- void
- updateDirectory(d)
- gopherDirP d;
- {
- int s;
- gopherItemP gi = d->selectorItem;
- BOOLEAN fetchOK;
-
-
- if ( (s = GI_connectWithStatus(gi)) < 0) return;
-
- writeString(s, vStringValue(&(gi->selector)));
- writeString(s, EOL_STRING);
-
- showStatus("Reloading requested directory", STAT_DIRECTORY,
- gi->host, gi->port);
-
- fetchOK = GI_getGopherDir(s, d);
-
- close(s);
-
- if (! removeStatusPanel() ) {
-
- /* someone cancelled as the directory load finished */
-
- return;
-
- }
-
- if (! fetchOK) {
- /* failure to re-load the directory */
- showError(
- "There seems to be no information in this directory\n");
- return;
- }
-
- setDirTime(d);
-
- return;
- }
-
-
- /* ====================================================================== */
- /* */
- /* Utilities used by the item class and its subclasses */
- /* */
- /* ====================================================================== */
-
-
- /* GU_ftpCheck
- return True (ok) if ftp is allowed or this item doesn't represent
- an ftp request. Return false if this this is an ftp request and
- ftp's are not allowed. */
-
- static BOOLEAN
- GU_ftpCheck(gi)
- gopherItemP gi;
- {
- BOOLEAN isFtp = FALSE;
-
- if ( appResources->allowFtp ) return TRUE;
-
- isFtp = (strncmp(vStringValue(&(gi->selector)), "ftp:", 4) == 0);
-
- /* don't see an "ftp:", but see if there's an '@' in the path */
- if (! isFtp) {
- isFtp = (index(vStringValue(&(gi->selector)), '@') != NULL);
- }
-
- return ( ! isFtp );
- }
-
-
- /* GU_makePrefix
- edit application resources of directory listing prefixes to conform
- to be blank padded and exactly PREFIX_LEN characters.
- NOTE: These strings are *NOT* NULL-terminated. */
-
- void
- GU_makePrefix(target, source)
- char *target, *source;
- {
- int i;
-
- for (i=0; i<PREFIX_LEN; i++) {
- if (*source == NULLC) {
- *(target++) = ' ';
- } else {
- *(target++) = *(source++);
- }
- }
- }
-
-
- /* GU_getGopherItem
- receive a response from a gopher server, parsing it as it comes in.
- Return the item pointer for success, NULL for failure. */
-
- static gopherItemP
- GU_getGopherItem(gfd)
- int gfd;
- {
- char buffer[255];
- char gType,
- gName[USER_STRING_LEN],
- gPath[SELECTOR_STRING_MAX_LEN],
- gHost[HOST_STRING_LEN];
- int gPort;
- BOOLEAN gPlus;
-
-
- if (readn(gfd, &(buffer[0]), 1) <= 0) {
- /* immediate EOF or error -- no item read */
- return NULL;
- }
-
- /** Get the kind of file from the first character **/
-
- gType = buffer[0];
-
- if (gType == A_EOI) return NULL;
-
- if (readField(gfd, &(buffer[1]), 255) <= 0) {
- /* immediate EOF or error -- no item read */
- return NULL;
- }
-
- /* get the User Displayable name */
-
- strncpy(gName, buffer + 1, USER_STRING_LEN);
-
- /* terminate the string just in case it was too long */
-
- gName[USER_STRING_LEN - 1] = '\0';
-
- /* get the Pathname */
-
- if (readField(gfd, gPath, SELECTOR_STRING_MAX_LEN) <= 0) {
- /* EOF or error after TYPE/NAME --
- no complete item read */
- return NULL;
- }
-
- /* get the hostname */
-
- if (readField(gfd, gHost, HOST_STRING_LEN) == 0) {
- /* EOF or error after TYPE/NAME/PATH --
- no complete item read */
- return NULL;
- }
-
- /* get the port number */
-
- if (readLine(gfd, buffer, 255)<=0) {
- /* EOF or error after TYPE/NAME/PATH --
- Problem - should be a port number here. But this
- isn't as serious as above. We'll try to proceed. */
- ;
- }
-
- gPort = 0;
-
- /* Get the port number */
-
- gPort = atoi(buffer);
-
- gPlus = False;
-
- return makeItem(gType, gName, gPath, gHost, gPort, gPlus);
- }
-
-
- /* GU_copySubclassRecord
- return a copy of the class record for subclass identified.
- Search both knownTypes, and types already initialized. */
-
- scInfo *
- GU_copySubclassRecord(t)
- char t;
- {
- scInfo *scRec;
- giSubclass *scID;
-
- scRec = (scInfo *) malloc(sizeof(scInfo));
-
- /* first check initialized types. This allows an alias
- to preferentially use a new external type. */
-
- for (scID = activeSubclass;
- scID != NULL && scID->typeID != t; scID=scID->next) ;
-
- if (scID == NULL) {
- /* it will be in this list or the final item type, unknown
- will be used */
- for (scID = definedSubclass;
- scID->typeID != t && scID->typeID != '\0'; scID++) ;
- }
-
- bcopy((char *) scID->subclass, (char *) scRec, sizeof(scInfo));
-
- return scRec;
- }
-
-
- /* GU_registerNewType
- Add a new type identifier and its subclass record to the known list. */
-
- void
- GU_registerNewType(t, scRec)
- char t;
- scInfo *scRec;
- {
- giSubclass *newSC = (giSubclass *) malloc (sizeof (giSubclass));
- giSubclass *sc;
-
- newSC->typeID = t;
- newSC->subclass = scRec;
- newSC->next = (giSubclass *) NULL;
-
- /* add to the end of the list for expected behavior */
-
- if (activeSubclass == (giSubclass *) NULL) {
- activeSubclass = newSC;
- } else {
- for (sc=activeSubclass; sc != NULL; sc=sc->next) {
- if (sc->typeID == t) { /* already is one */
- break;
- }
-
- if (sc->next == (giSubclass *) NULL) {
- sc->next = newSC;
- break;
- }
- }
- }
- }
-
-
- /* GU_isVowel
- return True if first character of a string is a vowel, false otherwise */
-
- static char vowels[] = "aAeEiIoOuU";
-
- BOOLEAN
- GU_isVowel(str)
- char *str;
- {
- char *v;
- for (v=vowels; (*str != *v && *v != NULLC); v++ );
-
- return (*v != NULLC);
- }
-
- /* Ideas for the implementation of the access limiting mechanism
- came from the University of Minnesota gopher server code.
- In particular, the matching of IP addresses and host name
- matching algorithm is from that code. */
-
-
- /* GU_addHostToAccessList
- add the host name or address to an access list.
- Determine if a given string is a name or IP address by
- checking the first character to see if it is numeric. */
-
- static void
- GU_addHostToAccessList(list, host)
- accessList *list;
- char *host;
- {
- char *temp;
- accessList addr;
-
- temp = (char *) malloc((strlen(host) + 1) * sizeof(char));
- strcpy(temp, host);
- addr = (accessListItem *) malloc(sizeof(accessListItem));
- addr->address = temp;
- addr->numeric = isdigit(*host) ? TRUE : FALSE;
-
- /* For matching, it should not matter whether the list is
- created in the same order as specified or reverse order,
- so each item in the input list will be added to the front. */
-
- addr->next = *list;
-
- *list = addr;
-
- return;
- }
-
-
- /* GU_createAccessList
- create an access list from a string of hostname/IP addresses */
-
- accessList
- GU_createAccessList(string)
- char *string;
- {
- /* pull out host or domain names separated by white space */
-
- char *cp;
- char name[256];
- char *np;
- accessList list = (accessList) NULL;
-
- cp = string;
- while (*cp != '\0') {
- while (isspace(*cp) && *cp != '\0') { cp++; };
- if ( *cp == '\0' ) break;
-
- np = &(name[0]);
- while (! isspace(*cp) && *cp != '\0') {
- *np = *cp;
- np++;
- cp++;
- }
- *np='\0';
-
- GU_addHostToAccessList(&list, name);
- }
-
- return list;
- }
-
-
- /* GU_checkAccess
- see if a proposed host access is allowed */
-
- BOOLEAN
- GU_checkAccess(host, permitList)
- char *host;
- accessList permitList;
- {
- accessListItem *allowed;
- int hostLen, addrLen;
-
- if (permitList == NULL) return TRUE;
-
- for (allowed=permitList; allowed!=NULL; allowed=allowed->next) {
- hostLen = strlen(host);
- addrLen = strlen(allowed->address);
- if (allowed->numeric) {
-
- /* numeric IP address -- compare from start */
-
- if (addrLen <= hostLen) {
- if (strncmp(host, allowed->address, addrLen) == 0) {
- return TRUE;
- }
-
- }
- } else {
-
- /* character host/domain name -- compare from end */
-
- if (addrLen <= hostLen) {
- if (strcasecmp((host+hostLen-addrLen),
- allowed->address) == 0) {
- return TRUE;
- }
- }
- }
- }
-
- return FALSE;
- }
-
-
- #ifdef DEBUG
- void
- GU_printAccessList(list)
- accessList list;
- {
- accessListItem *l;
-
- fprintf (stdout, "Host Access List:\n");
-
- for (l=list; l != (accessList) NULL; l=l->next) {
- fprintf (stdout, "\t%s %s\n", l->address,
- l->numeric ? "(IP address)" : "(host name)");
- }
-
- fprintf (stdout, " End of Host Access List\n");
- }
- #endif /* DEBUG */
-
-
- /* ====================================================================== */
- /* */
- /* Class methods and common subclass methods of gopher item */
- /* */
- /* ====================================================================== */
-
-
- /* GI_connnectWithStatus
- make a socket connection, posting the status popup and leaving it
- posted for the following processing */
-
- int
- GI_connectWithStatus(gi)
- gopherItemP gi;
- {
- int s;
-
- showStatus("Connecting to Gopher", STAT_CONNECT,
- gi->host, gi->port);
- s = connectToSocket(gi->host, gi->port);
- if (s < 0) {
-
- (void) removeStatusPanel();
-
- /* could not make a network connection to receive file */
-
- networkError(s, gi->host, gi->port);
- return -1;
- }
-
- /* check for cancel during connection */
-
- if (!updateStatusPanel(1, 0)) {
- (void) removeStatusPanel();
-
- close(s);
- return -1;
- }
-
- return s;
- }
-
-
- /* GI_getGopherDir
- receive a directory from a gopher server.
- Return the directory pointer for success, NULL for failure. */
-
- BOOLEAN
- GI_getGopherDir(gfd, d)
- int gfd;
- gopherDirP d;
- {
- gopherItemP gi;
- char buffer[512];
- int j, n=0;
- BOOLEAN goOn;
-
- /* get each item; for each one, decide if it should be kept
- in the directory. */
-
-
- while ( (gi = GU_getGopherItem(gfd)) != NULL ) {
-
- n++;
-
- if (appResources->showItems == showAll) {
- appendItem(&(d->contents), gi);
- }
-
- else if (appResources->showItems == showKnown &&
- ! UNKNOWN_TYPE(gi)) {
- appendItem(&(d->contents), gi);
- }
-
- else if (appResources->showItems == showAccessible &&
- gi->accessOk) {
- appendItem(&(d->contents), gi);
- }
-
- else if (appResources->showItems == showAvailable &&
- ! UNKNOWN_TYPE(gi) && gi->accessOk) {
- appendItem(&(d->contents), gi);
- }
-
- else {
- freeItem(gi);
- n--;
- }
-
- if (n % 5 == 0) {
- goOn = updateStatusPanel(n, 0);
- if (!goOn) {
- freeItemList(&(d->contents));
- break;
- }
- }
- }
-
- return (itemListLength(&(d->contents)) != 0);
- }
-
-
- /* GI_copyFromNet
- Generic copy setup. Makes network connection, updates status
- and invokes a type-specific copy procedure. */
-
- BOOLEAN
- GI_copyFromNet(outFD, gi, specialMsg)
- int outFD;
- gopherItemP gi;
- char *specialMsg;
- {
- int s;
- char message[MESSAGE_STRING_LEN];
- BOOLEAN rc, goOn = TRUE;
-
- if (gi->sc->copyDataType == copyTypeNone) return;
-
- if (specialMsg == (char *) NULL) {
- sprintf(message, "Copying the %s", gi->sc->typeName);
- } else {
- strcpy(message, specialMsg);
- }
-
- if ((s = GI_connectWithStatus(gi)) < 0) return FALSE;
-
- writeString(s, vStringValue(&(gi->selector)));
- writeString(s, EOL_STRING);
-
- switch (gi->sc->copyDataType) {
-
- case copyTypeAscii:
- rc = GI_copyAsciiFromNet(s, outFD, gi, message);
- break;
-
- case copyTypeBinaryEOF:
- rc = GI_copyBinaryFromNetEOF(s, outFD, gi, message);
- break;
-
- default:
- rc = FALSE;
- break;
- }
-
- close(s);
-
- /* check for cancel */
-
- goOn = removeStatusPanel();
-
- return (rc && goOn);
- }
-
-
-
- #define MIN_OF(a,b) ((a)<(b) ? (a) : (b))
- #define BIN_BUFFER_SIZE 1400
-
- /* GI_copyBinaryFromNetEOF
- Binary copy the contents of one open file descriptor to another.
- Copy until EOF
-
- The file descriptor references on open file for the result.
- For input, we will assume blocking I/O, so a read will always
- return something if it's not yet eof. */
-
- BOOLEAN
- GI_copyBinaryFromNetEOF(s, outFD, gi, msg)
- int s, outFD;
- gopherItemP gi;
- char *msg;
- {
- char buf[BIN_BUFFER_SIZE];
- int nr, nw;
- int byteCt=0;
- BOOLEAN goOn = TRUE;
-
- showStatus(msg, STAT_BINARY,
- gi->host, gi->port);
-
- while (TRUE) {
- nr = read(s, buf, BIN_BUFFER_SIZE);
-
- if (nr <= 0) {
- if (nr == -1) {
- perror("GI_copyBinaryFromNetEOF");
- fprintf (stderr,
- "Error copying binary data - input.\n");
- }
- break;
- }
-
- byteCt += nr;
- goOn = updateStatusPanel(byteCt, 0);
- if (!goOn) {
- break;
- }
-
- nw = write(outFD, buf, nr);
- if (nw != nr || nw < 0) {
- perror("GI_copyBinaryFromNetEOF");
- fprintf (stderr,
- "Error copying binary data - output.\n");
- break;
- }
- }
-
- return goOn;
- }
-
-
- /* GI_copyBinaryFromNetLen
- Binary copy the contents of one open file descriptor to another.
- Copy until a specific number of characters has been copied.
-
- The file descriptor references on open file for the result.
- For input, we will assume blocking I/O, so a read will always
- return something if it's not yet eof. */
-
- BOOLEAN
- GI_copyBinaryFromNetLen(s, outFD, gi, msg)
- int s, outFD;
- gopherItemP gi;
- char *msg;
- {
- char buf[BIN_BUFFER_SIZE];
- int nr, nw;
- int byteCt=0;
- BOOLEAN goOn = TRUE;
- int nBytes = 0;
-
-
- showStatus(msg, STAT_BINARY,
- gi->host, gi->port);
-
- while (nBytes > 0) {
- nr = read(s, buf, MIN_OF(nBytes, BIN_BUFFER_SIZE));
-
- if (nr <= 0) {
- if (nr == -1) {
- perror("GI_copyBinaryFromNetLen");
- fprintf (stderr,
- "Error copying binary data - input.\n");
- }
- break;
- }
-
- nBytes -= nr;
-
- byteCt += nr;
- goOn = updateStatusPanel(byteCt, 0);
- if (!goOn) {
- break;
- }
-
- nw = write(outFD, buf, nr);
- if (nw != nr || nw < 0) {
- perror("GI_copyBinaryFromNetLen");
- fprintf (stderr,
- "Error copying binary data - output.\n");
- break;
- }
- }
-
- return goOn;
- }
-
-
- /* GI_copyAsciiFromNet
- Copy the contents of one open text file descriptor to another. Copy
- until a line with a single period in column one or an end of file.
-
- Change "network ASCII" with CR-LF ending each line to standard
- Unix text files with a NL at the end of each line.
-
- Either file descriptor may reference a file or a socket connection.
- For sockets, we will assume blocking I/O, so a read will always
- return something if it's not yet eof, and a write will write everything. */
-
- static char newLine[1] = {NL};
-
- BOOLEAN
- GI_copyAsciiFromNet(s, outFD, gi, msg)
- int s, outFD;
- gopherItemP gi;
- char *msg;
- {
- char line[FILE_LINE_LEN];
- int len;
- int lineCt=0, wordCt=0;
- BOOLEAN goOn = TRUE;
-
-
- showStatus(msg, STAT_ASCII,
- gi->host, gi->port);
-
- if (readLine(s, line, FILE_LINE_LEN) <= 0) {
- (void) removeStatusPanel();
- return FALSE;
- }
- zapCRLF(line);
-
-
- while ( line[0] != '.' || line[1] != '\0') {
-
- lineCt++;
- if ( (lineCt < 50 && lineCt % 10 == 0) ||
- (lineCt < 500 && lineCt % 50 == 0) ||
- lineCt % 100 == 0 ) {
- goOn = updateStatusPanel(lineCt, 0);
- if (!goOn) {
- break;
- }
- }
-
- len = strlen(line);
- write(outFD, line, len);
- write(outFD, newLine, 1);
-
- if (readLine(s, line, FILE_LINE_LEN) <= 0) break;
- zapCRLF(line);
- }
-
- return goOn;
- }
-
-
-
- #define NET_BUFFER_SIZE 1400
-
- /* GI_copyNetUntilEOF
- copy chunks of data from a network file descriptor to a file pointer.
- No termination condition is expected except EOF on the input fd.
- This is a blocking read. */
-
- void
- GI_copyNetUntilEOF(s, fp)
- int s;
- FILE *fp;
- {
- char buf[NET_BUFFER_SIZE];
- int j;
-
- while (TRUE) {
- j = read(s, buf, NET_BUFFER_SIZE);
-
-
- if (j <= 0) {
- if (j == -1)
- fprintf (stderr,
- "Error (%d) copying data from the network\n",
- errno);
- break;
- }
-
- if (fwrite(buf, 1, j, fp) != j) {
- return;
- }
- }
- }
-
-
- /* GI_noCopyItem
- default "can't copy" procedure */
-
- BOOLEAN
- GI_noCopyItem(gi)
- gopherItemP gi;
- {
- int s;
- char *lastName;
- char message[MESSAGE_STRING_LEN];
-
- sprintf(message, "You cannot copy %s %s to a file.",
- (GU_isVowel(gi->sc->typeName) ? "an" : "a"), gi->sc->typeName);
- showError(message);
-
- return FALSE;
- }
-
-
- /* GI_copyToFile
- copy a gopher item directly to a file */
-
- BOOLEAN
- GI_copyToFile(gi)
- gopherItemP gi;
- {
-
- saveNetRequest(gi);
- return TRUE;
- }
-
-
- /* GI_access
- check the access limits for an item. This class method will be
- overridden for many subclasses which need to check the application
- resources for permission. */
-
- BOOLEAN
- GI_access(gi)
- gopherItemP gi;
- {
- BOOLEAN result;
-
- result = GU_ftpCheck(gi);
-
- return result;
- }
-
-
- /* GI_init
- default class initialization procedure a subclass of Gopher Item. This
- procedure works for class "unknown". */
-
- void
- GI_init()
- {
- GU_makePrefix(prefixUnknown, appResources->prefixUnknown);
- GU_registerNewType(A_UNKNOWN, &noSubclass);
-
- return;
- }
-
-
- /* GI_done
- default class termination procedure a subclass of Gopher Item. This
- procedure is a no-op. */
-
- void
- GI_done()
- {
- return;
- }
-
-
- /* GI_restart
- default restart cleanup for a subclass of Gopher Item. This
- procedure is a no-op. */
-
- void
- GI_restart()
- {
- return;
- }
-
-
- /* GI_process
- default processing procedure for a subclass of Gopher Item. This
- procedure is a no-op, suggesting an alternative means of processing
- the item. Every known item type should override this procedure. */
-
- BOOLEAN
- GI_process(gi)
- gopherItemP gi;
- {
- char message[MESSAGE_STRING_LEN];
-
- sprintf(message,
- "There is no automatic processing for this gopher item type (%c)",
- gi->type);
- strcat(message,
- "\nYou may be able to use the Copy command to access the file.");
- showError(message);
-
- return FALSE;
- }
-